#include "Utils.h"
#include <Windows.h>
#include <sstream>
#include <bitset>

namespace GPUCC
{
	std::string Utils::GetFullPath(const std::string& Path)
	{
		std::string FullPath("");
		char FullPathBuffer[MAX_PATH + 1];

		ZeroMemory(FullPathBuffer, MAX_PATH + 1);

		DWORD Length = GetFullPathName(Path.c_str(), MAX_PATH, FullPathBuffer, NULL);

		if (Length < MAX_PATH + 1)
		{
			FullPath.assign(FullPathBuffer);
		}

		return (FullPath);
	}

	bool Utils::FileExists(const std::string& Path)
	{
		DWORD Attributes = GetFileAttributesA(Path.c_str());

		return ((Attributes != INVALID_FILE_ATTRIBUTES) &&
				(!(Attributes & FILE_ATTRIBUTE_DIRECTORY)));
	}

	bool Utils::DirectoryExists(const std::string& Path)
	{
		DWORD Attributes = GetFileAttributesA(Path.c_str());

		return ((Attributes != INVALID_FILE_ATTRIBUTES) &&
				(Attributes & FILE_ATTRIBUTE_DIRECTORY));
	}

	std::vector<std::string> Utils::GetAllFilesNames(std::string FolderPath, const std::string& Filter)
	{
		WIN32_FIND_DATA ffd;
		HANDLE hFind = INVALID_HANDLE_VALUE;
		std::vector<std::string> FileNames;

		FolderPath.append("\\");
		FolderPath = GetFullPath(FolderPath);
		FolderPath.append(Filter);

		hFind = FindFirstFile(FolderPath.c_str(), &ffd);

		if (hFind != INVALID_HANDLE_VALUE)
		{
			do
			{
				if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
				{
					FileNames.push_back(ffd.cFileName);
				}
			} while (FindNextFile(hFind, &ffd) != 0);

			FindClose(hFind);
		}

		return (FileNames);
	}

	std::vector<std::string> Utils::SplitString(const std::string& Value, const char Seperator)
	{
		std::vector<std::string> Substrings;
	    std::istringstream Stream(Value);
	    std::string CurrentSubstring;

		while (std::getline(Stream, CurrentSubstring, Seperator))
		{
			Substrings.push_back(CurrentSubstring);
		}

		return (Substrings);
	}

	std::vector<unsigned char> Utils::ConvertHexToBytes(const std::string& Value)
	{
		std::vector<unsigned char> Bytes;
		std::vector<std::pair<const char, const char>> Limits;

		Limits.push_back(std::make_pair('0', '9'));
		Limits.push_back(std::make_pair('A', 'Z'));
		Limits.push_back(std::make_pair('a', 'z'));

		if ((Value.length() == 0) || (Value.length() % 2 != 0))
		{
			throw std::exception("Can't convert invalid length string!");
		}
		else if (!ValidateCharsRanges(Value, Limits))
		{
			throw std::exception("Can't convert non-ascii hex!");
		}
		else
		{
			std::istringstream HexStream(Value);
			std::stringstream CurrentByteStream;

			while (!HexStream.eof())
			{
				unsigned char CurrentByte;
				HexStream >> std::hex >> CurrentByte;
				CurrentByteStream << std::hex << CurrentByte;

				if (!HexStream.eof())
				{
					HexStream >> std::hex >> CurrentByte;		
					CurrentByteStream << std::hex << CurrentByte;

					unsigned int CurrentByteValue;
					CurrentByteStream >> CurrentByteValue;

					Bytes.push_back(CurrentByteValue);

					CurrentByteStream.clear();
				}
			} 
		}

		return (Bytes);
	}

	unsigned int Utils::ConvertStringToUnsignedInt(const std::string& Value)
	{
		unsigned int ConvertedValue = 0;

		if (!ValidateCharsRange(Value, '0', '9'))
		{
			throw std::exception("Can't convert negative or not a number!");
		}
		else
		{
			std::istringstream Stream(Value);

			Stream >> ConvertedValue;
		}

		return (ConvertedValue);
	}

	void Utils::ConvertFromTotalSeconds(unsigned int TotalElapsedSeconds, unsigned int& ElapsedHours, unsigned int& ElapsedMinutes, unsigned int& ElapsedSeconds)
	{
		ElapsedHours = TotalElapsedSeconds / 60 / 60;
		ElapsedMinutes = (unsigned int)(TotalElapsedSeconds / 60) % 60;
		ElapsedSeconds = (unsigned int)TotalElapsedSeconds % 60;
	}
	
	bool Utils::ValidateCharsRanges(const std::string& Value, const std::vector<std::pair<const char, const char>>& Limits)
	{
		bool Valid = true;
		std::istringstream Stream(Value);
		char CurrentChar;

		while ((!Stream.eof()) && (Valid))
		{
			Stream >> CurrentChar;

			Valid = false;
			for (std::vector<std::pair<const char, const char>>::const_iterator CurrentLimitPair = Limits.begin();
				((CurrentLimitPair != Limits.end()) && (!Valid)); ++CurrentLimitPair)
			{
				std::string CurrentCharString(1, CurrentChar);

				Valid = ValidateCharsRange(CurrentCharString, CurrentLimitPair->first, CurrentLimitPair->second);
			}
		}

		return (Valid);
	}

	bool Utils::ValidateCharsRange(const std::string& Value, const char LowLimit, const char HighLimit)
	{
		bool Valid = true;
		std::istringstream Stream(Value);
		char CurrentChar;

		while ((!Stream.eof()) && (Valid))
		{
			Stream >> CurrentChar;

			Valid = ((CurrentChar >= LowLimit) && (CurrentChar <= HighLimit));
		}

		return (Valid);
	}
}